func(app *App)HandleMutate(w http.ResponseWriter, r *http.Request) { admissionReview := &admissionv1.AdmissionReview{}
// read the AdmissionReview from the request json body err := readJSON(r, admissionReview) if err != nil { app.HandleError(w, r, err) return }
// unmarshal the pod from the AdmissionRequest pod := &corev1.Pod{} if err := json.Unmarshal(admissionReview.Request.Object.Raw, pod); err != nil { app.HandleError(w, r, fmt.Errorf("unmarshal to pod: %v", err)) return }
// add the volume to the pod pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ Name: "hello-volume", VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ Name: "hello-configmap", }, }, }, })
// add volume mount to all containers in the pod for i := 0; i < len(pod.Spec.Containers); i++ { pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts, corev1.VolumeMount{ Name: "hello-volume", MountPath: "/etc/config", }) }
┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectl apply -k ./ serviceaccount/webhook-cert-sa created clusterrole.rbac.authorization.k8s.io/webhook-cert-cluster-role created clusterrolebinding.rbac.authorization.k8s.io/webhook-cert-cluster-role-binding created job.batch/webhook-cert-setup created ┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectl apply -k ../other/ configmap/hello-configmap created service/hello-webhook-service created mutatingwebhookconfiguration.admissionregistration.k8s.io/hello-webhook.leclouddev.com created ┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectl apply -k ../deployment/ deployment.apps/hello-webhook-deployment created ┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectl config set-context --current --namespace mutating-webhook Context "kubernetes-admin@kubernetes" modified.
webhook 一直没办法正常运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectl get all NAME READY STATUS RESTARTS AGE pod/hello-webhook-deployment-7f599b95c4-pg9w5 0/1 ContainerCreating 0 55s pod/webhook-cert-setup-v62rt 0/1 CrashLoopBackOff 2 (15s ago) 79s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/hello-webhook-service ClusterIP 10.103.24.30 <none> 443/TCP 59s
NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/hello-webhook-deployment 0/1 1 0 55s
NAME DESIRED CURRENT READY AGE replicaset.apps/hello-webhook-deployment-7f599b95c4 1 1 0 55s
NAME COMPLETIONS DURATION AGE job.batch/webhook-cert-setup 0/1 79s 79s ┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$
┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$cat generate_certificate.sh #!/usr/bin/env sh
set -e
usage() { cat <<EOF Generate certificate suitable for use with any Kubernetes Mutating Webhook. This script uses k8s' CertificateSigningRequest API to a generate a certificate signed by k8s CA suitable for use with any Kubernetes Mutating Webhook service pod. This requires permissions to create and approve CSR. See https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster for detailed explantion and additional instructions. The server key/cert k8s CA cert are stored in a k8s secret. usage: ${0} [OPTIONS] The following flags are required. --service Service name of webhook. --webhook Webhook config name. --namespace Namespace where webhook service and secret reside. --secret Secret name for CA certificate and server certificate/key pair. The following flags are optional. --webhook-kind Webhook kind, either MutatingWebhookConfiguration or ValidatingWebhookConfiguration (defaults to MutatingWebhookConfiguration) EOF exit 1 }
[ -z "${service}" ] && echo"ERROR: --service flag is required" && exit 1 [ -z "${webhook}" ] && echo"ERROR: --webhook flag is required" && exit 1 [ -z "${secret}" ] && echo"ERROR: --secret flag is required" && exit 1 [ -z "${namespace}" ] && echo"ERROR: --namespace flag is required" && exit 1
fullServiceDomain="${service}.${namespace}.svc"
# THE CN has a limit of 64 characters. We could remove the namespace and svc # and rely on the Subject Alternative Name (SAN), but there is a bug in EKS # that discards the SAN when signing the certificates. # # https://github.com/awslabs/amazon-eks-ami/issues/341 if [ ${#fullServiceDomain} -gt 64 ] ; then echo"ERROR: common name exceeds the 64 character limit: ${fullServiceDomain}" exit 1 fi
if [ ! -x "$(command -v openssl)" ]; then echo"ERROR: openssl not found" exit 1 fi
csrName=${service}.${namespace} tmpdir=$(mktemp -d) echo"creating certs in tmpdir ${tmpdir} "
cat <<EOF >> "${tmpdir}/csr.conf" [req] req_extensions = v3_req distinguished_name = req_distinguished_name [req_distinguished_name] [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = ${service} DNS.2 = ${service}.${namespace} DNS.3 = ${fullServiceDomain} DNS.4 = ${fullServiceDomain}.cluster.local EOF echo"/CN=${fullServiceDomain}" openssl genrsa -out "${tmpdir}/server-key.pem" 2048 #openssl req -new -key "${tmpdir}/server-key.pem" -subj "/CN=${fullServiceDomain}" -out "${tmpdir}/server.csr" -config "${tmpdir}/csr.conf" openssl req -new -key "${tmpdir}/server-key.pem" -subj "/CN=system:node:${fullServiceDomain};/O=system:nodes" -out "${tmpdir}/server.csr" -config "${tmpdir}/csr.conf" set +e # clean-up any previously created CSR for our service. Ignore errors if not present. if kubectl delete csr "${csrName}"; then echo"WARN: Previous CSR was found and removed." fi set -e
# create server cert/key CSR and send it to k8s api cat <<EOF | kubectl create -f - apiVersion: certificates.k8s.io/v1 kind: CertificateSigningRequest metadata: name: ${csrName} spec: #signerName: kubernetes.io/kube-apiserver-client signerName: kubernetes.io/kubelet-serving groups: - system:authenticated request: $(base64 < "${tmpdir}/server.csr" | tr -d '\n') usages: - server auth - digital signature - key encipherment EOF
set +e # verify CSR has been created whiletrue; do if kubectl get csr "${csrName}"; then echo"CertificateSigningRequest create succsee" break fi done set -e
# approve and fetch the signed certificate . !! not working with k8s 1.19.1, running the command separately outside of the container / node set +e whiletrue; do if kubectl certificate approve "${csrName}"; then echo"${csrName} certificate approve" break fi done
set -e
set +e # verify certificate has been signed i=1 while [ "$i" -ne 10 ] do serverCert=$(kubectl get csr "${csrName}" -o jsonpath='{.status.certificate}') if [ "${serverCert}" != '' ]; then break fi sleep 5 i=$((i + 1)) done
set -e if [ "${serverCert}" = '' ]; then echo"ERROR: After approving csr ${csrName}, the signed certificate did not appear on the resource. Giving up after 10 attempts." >&2 exit 1 fi
echo"${serverCert}" | openssl base64 -d -A -out "${tmpdir}/server-cert.pem"
# create the secret with CA cert and server cert/key kubectl create secret tls "${secret}" \ --key="${tmpdir}/server-key.pem" \ --cert="${tmpdir}/server-cert.pem" \ --dry-run -o yaml | kubectl -n "${namespace}" apply -f -
#caBundle=$(base64 < /run/secrets/kubernetes.io/serviceaccount/ca.crt | tr -d '\n') caBundle=$(cat ${tmpdir}/server-cert.pem) set +e # Patch the webhook adding the caBundle. It uses an `add` operation to avoid errors in OpenShift because it doesn't set # a default value of empty string like Kubernetes. Instead, it doesn't create the caBundle key. # As the webhook is not created yet (the process should be done manually right after this job is created), # the job will not end until the webhook is patched. whiletrue; do echo"INFO: Trying to patch webhook adding the caBundle." if kubectl patch "${kind:-mutatingwebhookconfiguration}""${webhook}" --type='json' -p "[{'op': 'add', 'path': '/webhooks/0/clientConfig/caBundle', 'value':'${serverCert}'}]"; then break fi echo"INFO: webhook not patched. Retrying in 5s..." sleep 5 done ┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$
┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$./generate_certificate.sh --service webhook-svc --webhook hello-webhook.leclouddev.com --secret hello-tls-secret --namespace mutating-webhook creating certs in tmpdir /tmp/tmp.Di367dgyMz /CN=webhook-svc.mutating-webhook.svc Generating RSA private key, 2048 bit long modulus ........................................+++ .........+++ e is 65537 (0x10001) Error from server (NotFound): certificatesigningrequests.certificates.k8s.io "webhook-svc.mutating-webhook" not found certificatesigningrequest.certificates.k8s.io/webhook-svc.mutating-webhook created NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION webhook-svc.mutating-webhook 0s kubernetes.io/kubelet-serving kubernetes-admin <none> Pending CertificateSigningRequest create succsee certificatesigningrequest.certificates.k8s.io/webhook-svc.mutating-webhook approved webhook-svc.mutating-webhook certificate approve W1115 17:25:40.403890 66211 helpers.go:663] --dry-run is deprecated and can be replaced with --dry-run=client. secret/hello-tls-secret created INFO: Trying to patch webhook adding the caBundle. mutatingwebhookconfiguration.admissionregistration.k8s.io/hello-webhook.leclouddev.com patched
如果 命名空间或者 svc 名字太长的话,会报下面的错,需要调整短一点
1 2 3 4 5 6 7 8 9 10
┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$./generate_certificate.sh --service hello-webhook-service --webhook hello-webhook.leclouddev.com --secret hello-tls-secret --namespace k8s-hello-mutating-webhook creating certs in tmpdir /tmp/tmp.GcNh5TwKXP /CN=hello-webhook-service.k8s-hello-mutating-webhook.svc Generating RSA private key, 2048 bit long modulus ....+++ ...................................................+++ e is 65537 (0x10001) problems making Certificate Request 140682165290896:error:0D07A097:asn1 encoding routines:ASN1_mbstring_ncopy:string too long:a_mbstr.c:158:maxsize=64
测试结果
创建指定标签的 Pod ,自动挂载 CM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectl run busybox-1 --image=busybox --restart=Never -l=app=busybox,hello=true -- sleep 3600 pod/busybox-1 created ┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectlexec busybox-1 -it -- sh -c "ls /etc/config/hello.txt" /etc/config/hello.txt ┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectlexec busybox-1 -it -- sh -c "cat /etc/config/hello.txt"
┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectl run busybox-2 --image=busybox --restart=Never -l=app=busybox -- sleep 3600 pod/busybox-2 created ┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectl get pod -l=app=busybox -L=hello-added NAME READY STATUS RESTARTS AGE HELLO-ADDED busybox-1 1/1 Running 0 30m OK busybox-2 1/1 Running 0 28s ┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$kubectlexec busybox-2 -it -- sh -c "ls /etc/config/hello.txt" ls: /etc/config/hello.txt: No such file or directory command terminated with exit code 1 ┌──[root@vms100.liruilongs.github.io]-[~/ansible/k8s-hello-mutating-webhook/k8s-hello-mutating-webhook/k8s/csr] └─$